# -*- coding: utf-8 -*-
"""
    .filename
    ~~~~~~~~~~~~~~

    Description

    :create by: lyncir
    :date: 2021-03-30 10:25:48 (+0800)
    :last modified date: 2021-05-10 21:03:26 (+0800)
    :last modified by: lyncir
"""
import random
from collections import namedtuple, Counter

import pyshare.utils.json_utils as json
from common.service_config import get_app_id, get_ctx_time
from pyshare.const.enums.cross_siegelord_enum import CrossSiegelordCacheKey, \
    TeamStatus, TeamType, RoleTeamId, SiegelordBuffType, NpcSeatId, \
    NpcServerId, TeamCount, InitDatetime
from pyshare.framework.redisutils.redis_storage import RedisStorage
from pyshare.dal.gameconfig.query import getProfile
from pyshare.utils.time_utils import to_timestamp, timestamp_to_str


Point = namedtuple("Point", ["x", "y"])


# 点的四个方向, 用于计算
point_directions = [
    Point(0, 1),
    Point(0, -1),
    Point(1, 0),
    Point(-1, 0),
]


# 点的四个角, 用于计算
point_diagonals = [
    Point(-1, -1),
    Point(1, 1),
    Point(-1, 1),
    Point(1, -1),
]


# 两点相加
def point_add(a, b):
    return Point(a.x + b.x, a.y + b.y)


# 两点相减
def point_subtract(a, b):
    return Point(a.x - b.x, a.y - b.y)


# 某点的单个方向邻居
def point_neighbor(point, direction):
    return point_add(point, point_directions[direction])


# 某点的单个方向对角
def point_diagonal_neighbor(point, direction):
    return point_add(point, point_diagonals[direction])


# 长度
def point_length(point):
    return abs(point.x) + abs(point.y)


# 两点间距离
def point_distance(a, b):
    return point_length(point_subtract(a, b))


# 中心点到N距离的所有点
def point_range(center, n):
    results = []

    for x in range(-n, n + 1):
        for y in range(-(n - abs(x)), (n - abs(x)) + 1):
            results.append(point_add(center, Point(x, y)))

    return results


# 中心点到N距离的所有点(地图范围内)
def point_range_on_map(center, n, map_size):
    w, h = map_size
    results = []

    for x in range(-n, n + 1):
        for y in range(-(n - abs(x)), (n - abs(x)) + 1):
            point = point_add(center, Point(x, y))
            if (0 <= point.x < w) and (0 <= point.y < h):
                results.append(point)

    return results


def generate_random_points(area, num, terrain_size, area_id):
    """
    在矩形范围内获取随机的几个顶点

    -------------
    |p_min|     |
    |-----|-----|
    |     |p_max|
    -------------

    两个点(area)构成一个矩形区域,在矩形区域中生成num个大小的地形

    :param tuple(tuple) area: 区域列表
    :param int num: 生成数量
    :param tuple terrain_size: 地形大小, 长x宽
    """
    # 长,宽
    w, h = terrain_size

    # 最小坐标, 最大坐标
    (min_x, min_y), (max_x, max_y) = area

    # 区域内所有的符合范围的最小坐标点
    points = []

    # x坐标点
    xs = []
    for x in (list(range(min_x, max_x + 1))[::w]):
        # 点加上宽不能超过最大宽度
        if x + w <= max_x:
            xs.append(x)

    # y坐标点
    ys = []
    for y in (list(range(min_y, max_y + 1))[::h]):
        if y + h <= max_y:
            ys.append(y)

    for x in xs:
        for y in ys:
            points.append((x, y, area_id))

    # 随机取出num个点,并进行地形赋值
    if len(points) >= num:
        points = random.sample(points, k=num)

    return points


def get_point_stat_info(rid, seat_id, team_short_keys):
    """
    获取地块敌我盟数据

    :param str rid: 角色rid
    :param int seat_id: 角色国家位置
    :param list(str) team_short_keys: 队伍数据
    """
    # 敌
    enemy = 0
    # 我
    me = 0
    # 盟
    alliance = 0

    for team_short_key in team_short_keys:
        role_seat_id, role_guid, _ = team_short_key.split(":")
        if role_guid == rid:
            me += 1

        if (role_guid != rid) and (seat_id == int(role_seat_id)):
            alliance += 1

        if seat_id != int(role_seat_id):
            enemy += 1

    return enemy, me, alliance


class TeamNotFound(Exception):
    """
    自定义异常: 无法找到队伍
    """


class PointNotFound(Exception):
    """
    自定义异常: 无法找到地块
    """


class PointInfo(object):
    """
    地块信息
    """
    def __init__(self, activity_id, x, y):
        """
        """
        self.redis = RedisStorage.redis_conn_map['pluto']

        # 活动id: str
        self.activity_id = activity_id
        # x坐标: int
        self.x = x
        # y坐标: int
        self.y = y

        # 数据指向: str
        self.data_ref = None
        # 拥有者: int, 默认npc
        self.owner = NpcSeatId
        # 拥有者信息: {allise_id, country_id}
        self.owner_info = {}
        # 地形id: int
        self.terrain_id = None
        # 所属区域id: int
        self.area_id = None
        # 最新占领时间: int
        self.last_occupy_time = 0
        # 最后一击玩家信息 {区服, rid, 昵称, 头像, 头像框, 时装}
        self.last_occupy_role_info = {}
        # 停留的攻击队伍
        self.stay_atks = []
        # 停留的防守队伍
        self.stay_defs = []
        # 停留的NPC队伍
        self.stay_npcs = []
        # 地块配置
        self.cfg = None

    def save(self):
        """
        保存地块信息到缓存
        """
        assert self.terrain_id is not None
        assert self.area_id is not None

        # 保存地块信息
        point_info_key = CrossSiegelordCacheKey.PointInfo.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
        )

        data_ref = self.data_ref if self.data_ref is not None else ""

        result = {
            "terrain_id": self.terrain_id,
            "area_id": self.area_id,
            "data_ref": data_ref,
            "owner": self.owner,
            "last_occupy_time": self.last_occupy_time,
            "owner_info": json.dumps(self.owner_info),
            # 有中文
            "last_occupy_role_info": json.dumps(self.last_occupy_role_info, ensure_ascii=False),
        }

        self.redis.hmset(point_info_key, result)

    def load_less(self):
        """
        从缓存中加载尽量少的数据(移动地图使用)
        """
        x, y = self.x, self.y
        point_info_key = CrossSiegelordCacheKey.PointInfo.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=x,
            y=y,
        )

        data = self.redis.hgetall(point_info_key)
        if not data:
            raise PointNotFound

        self.terrain_id = int(data["terrain_id"])
        self.owner = int(data["owner"])
        self.area_id = int(data["area_id"])
        self.last_occupy_time = int(data["last_occupy_time"])
        self.last_occupy_role_info = json.loads(data["last_occupy_role_info"])
        self.owner_info = json.loads(data.get("owner_info")) if data.get("owner_info") else {}
        self.data_ref = data["data_ref"] if data["data_ref"] else None

        # 加载地块配置
        self.cfg = getProfile("TcCrossSiegelordTerrain", TerrainId=self.terrain_id)

    def load_stay(self, team_type):
        """
        加载停留的队伍

        :param int team_type: 队伍类型
        """
        # 停留的队伍
        stay_key = CrossSiegelordCacheKey.PointStay.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
            team_type=team_type,
        )

        results = self.redis.zrange(stay_key, 0, -1) or []

        if team_type == TeamType.Atk:
            self.stay_atks.extend(results)
        elif team_type == TeamType.Def:
            self.stay_defs.extend(results)
        elif team_type == TeamType.Npc:
            self.stay_npcs.extend(results)

        return results

    def get_stay_count(self, team_type):
        """
        获取队伍数量
        """
        stay_key = CrossSiegelordCacheKey.PointStay.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
            team_type=team_type,
        )

        count = self.redis.zcard(stay_key)
        return count

    def get_owner(self):
        """
        获取拥有者
        """
        point_info_key = CrossSiegelordCacheKey.PointInfo.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
        )

        owner = self.redis.hget(point_info_key, "owner")
        return int(owner)

    def load(self):
        """
        从缓存中加载

        加载地块信息和所有的队伍
        """
        self.load_less()

        for team_type in [TeamType.Atk, TeamType.Def, TeamType.Npc]:
            self.load_stay(team_type)

    def add_stay_team(self, team_type, team_key):
        """
        添加停留的队伍

        :param int team_type: 队伍类型
        :param str team_key: 队伍key
        """
        point_stay_key = CrossSiegelordCacheKey.PointStay.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
            team_type=team_type,
        )
        score = int(to_timestamp(get_ctx_time()))
        self.redis.zadd(point_stay_key, score, team_key)

        if team_type == TeamType.Atk:
            self.stay_atks.append(team_key)
        elif team_type == TeamType.Def:
            self.stay_defs.append(team_key)
        elif team_type == TeamType.Npc:
            self.stay_npcs.append(team_key)

    def remove_stay_team(self, team_type, team_key):
        """
        移除停留的队伍

        :param int team_type: 队伍类型
        :param str team_key: 队伍key
        """
        point_stay_key = CrossSiegelordCacheKey.PointStay.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
            team_type=team_type,
        )
        self.redis.zrem(point_stay_key, team_key)

        if team_type == TeamType.Atk and team_key in self.stay_atks:
            self.stay_atks.remove(team_key)
        elif team_type == TeamType.Def and team_key in self.stay_defs:
            self.stay_defs.remove(team_key)
        elif team_type == TeamType.Npc and team_key in self.stay_npcs:
            self.stay_npcs.remove(team_key)

    def is_adjacent(self, seat_id, distance=1):
        """
        地块的四周是否相邻某个国家

        :param int seat_id: 大本营位置
        :param int distance: 距离
        """
        from pyshare.dal.gameconfig.game_env import get_config_env_set

        # 获取地图大小
        map_size = get_config_env_set("CrossSiegelordMapSize")

        # 宽, 高
        width, height = self.cfg.TerrainSize

        if width > 1 or height > 1:
            multi_point = True
        else:
            multi_point = False

        # 是否相邻
        status = False

        # 起始点
        if self.data_ref is None:
            center = Point(self.x, self.y)
        else:
            x, y = map(int, self.data_ref.split(":"))
            center = Point(x, y)

        # 获取相邻的点
        if multi_point is False:
            neighbor_points = point_range_on_map(center, distance, map_size)

        else:
            points = []
            for w1 in range(width):
                for h1 in range(height):
                    x1 = center.x + w1
                    y1 = center.y + h1
                    points.append(Point(x1, y1))

            all_points = []
            for point in points:
                all_points.extend(point_range_on_map(point, distance, map_size))

            neighbor_points = [key for key, val in Counter(all_points).iteritems() if val == 1]

        # 判断相邻的点的拥有者是否一致
        for neighbor_point in neighbor_points:

            # 真实坐标
            real_x, real_y = neighbor_point.x, neighbor_point.y

            neighbor_key = CrossSiegelordCacheKey.PointInfo.format(
                app_id=get_app_id(),
                activity_id=self.activity_id,
                x=real_x,
                y=real_y,
            )

            data_ref = self.redis.hget(neighbor_key, "data_ref")

            # 数据指向
            if data_ref:
                real_x, real_y = map(int, data_ref.split(":"))
                neighbor_key = CrossSiegelordCacheKey.PointInfo.format(
                    app_id=get_app_id(),
                    activity_id=self.activity_id,
                    x=real_x,
                    y=real_y,
                )

            # 只获取拥有者
            owner = int(self.redis.hget(neighbor_key, "owner"))

            if owner == seat_id:
                status = True
                break

        return status

    def add_reosurces_to_country(self):
        """
        添加地块资源产出到国家
        """
        resources_key = CrossSiegelordCacheKey.CountryResources.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.owner,
        )

        # 添加盟国积分产出
        if self.cfg.OutAlliesScore > 0:
            self.redis.hincrby(resources_key, "allies_score", self.cfg.OutAlliesScore)

        # 添加商城货币产出
        if self.cfg.OutCoin > 0:
            self.redis.hincrby(resources_key, "coin", self.cfg.OutCoin)

        # 对地块数量进行+1
        self.redis.hincrby(resources_key, "terrain_id:{}".format(self.terrain_id), 1)

    def remove_reosurces_from_country(self, owner=None):
        """
        从国家中移除地块资源产出
        """
        if owner is None:
            owner = self.owner

        resources_key = CrossSiegelordCacheKey.CountryResources.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=owner,
        )

        # 添加盟国积分产出
        if self.cfg.OutAlliesScore > 0:
            self.redis.hincrby(resources_key, "allies_score", -self.cfg.OutAlliesScore)

        # 添加商城货币产出
        if self.cfg.OutCoin > 0:
            self.redis.hincrby(resources_key, "coin", -self.cfg.OutCoin)

        # 对地块数量进行-1
        self.redis.hincrby(resources_key, "terrain_id:{}".format(self.terrain_id), -1)

    def add_buff_to_country(self):
        """
        添加地块buff到国家
        """
        cur_buff_list = []

        country_buff_key = CrossSiegelordCacheKey.CountryBuff.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.owner,
        )

        for buff_id in self.cfg.Buff:
            # buff配置
            buff_cfg = getProfile("TcCrossSiegelordBuff", BuffId=buff_id)
            # 确认是国家buff
            if buff_cfg.BuffType in SiegelordBuffType.CountryBuff:
                # 非限制buff
                if buff_cfg.BuffType not in SiegelordBuffType.CountryTempBuff:
                    # [buff_id, 过期时间]
                    buff = json.dumps([buff_id, 0])

                    new_num = self.redis.hincrby(country_buff_key, buff, 1)
                    cur_buff = [buff_id, 0, new_num]
                    cur_buff_list.append(cur_buff)

        return cur_buff_list

    def remove_buff_from_country(self, owner=None):
        """
        从国家buff中移除地块buff
        """
        if owner is None:
            owner = self.owner

        country_buff_key = CrossSiegelordCacheKey.CountryBuff.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=owner,
        )

        for buff_id in self.cfg.Buff:
            # buff配置
            buff_cfg = getProfile("TcCrossSiegelordBuff", BuffId=buff_id)
            # 确认是国家buff
            if buff_cfg.BuffType in SiegelordBuffType.CountryBuff:
                # 非限制buff
                if buff_cfg.BuffType not in SiegelordBuffType.CountryTempBuff:
                    # [buff_id, 过期时间]
                    buff = json.dumps([buff_id, 0])

                    self.redis.hincrby(country_buff_key, buff, -1)

    def asdict(self):
        """
        移动地图使用(客户端)
        """
        return {
            "X": self.x,
            "Y": self.y,
            "Owner": self.owner,
            "LastOccupyRoleInfo": self.last_occupy_role_info,
            "LastOccupyTime": self.last_occupy_time,
            "LastOccupyTimeStr": timestamp_to_str(self.last_occupy_time) if self.last_occupy_time else InitDatetime,
        }

    # #################
    # 单独更新方法
    # #################
    def update_owner(self, owner):
        """
        更新地块拥有者

        :param int owner: 拥有者
        """
        self.owner = owner

        # 保存
        point_info_key = CrossSiegelordCacheKey.PointInfo.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
        )

        self.redis.hset(point_info_key, "owner", owner)

    def update_owner_info(self, owner_info):
        """
        更新地块拥有者信息

        :param int owner: 拥有者信息
        """
        self.owner_info = owner_info

        # 保存
        point_info_key = CrossSiegelordCacheKey.PointInfo.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
        )

        self.redis.hset(point_info_key, "owner_info", json.dumps(owner_info))

    def update_last_occupy_role_info(self, role_info):
        """
        更新最后占领者信息
        """
        self.last_occupy_role_info = role_info

        point_info_key = CrossSiegelordCacheKey.PointInfo.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
        )
        # 有中文
        self.redis.hset(point_info_key, "last_occupy_role_info", json.dumps(role_info, ensure_ascii=False))

    def update_last_occupy_time(self, timestamp):
        """
        更新最后占领时间
        """
        self.last_occupy_time = timestamp

        point_info_key = CrossSiegelordCacheKey.PointInfo.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            x=self.x,
            y=self.y,
        )
        self.redis.hset(point_info_key, "last_occupy_time", timestamp)


class Minister(object):
    """
    大臣
    """

    def __init__(self, minister_data):
        # minister_format: [大臣id, 爵位, 战力, 最大血量, 当前血量,
        #                   道具, 守护, 皮肤, 复活时间, 装备, 守护数据,
        #                   命格觉醒等级]
        self.id = minister_data[0]
        self.rank = minister_data[1]
        self.fight_power = minister_data[2]
        self.max_hp = minister_data[3]
        self.hp = minister_data[4]
        self.item = minister_data[5]
        self.adorn_guard_id = minister_data[6]
        self.current_costume = minister_data[7]
        self.refresh_time = minister_data[8]
        self.equipment = minister_data[9]
        self.guard_list = minister_data[10]
        self.destiny_wake_lv = minister_data[11]

        if len(minister_data) >= 13:
            self.refresh_time_str = minister_data[12]
        else:
            if self.refresh_time == 0:
                self.refresh_time_str = InitDatetime
            else:
                self.refresh_time_str = timestamp_to_str(self.refresh_time)

    def check_item(self, item_id, item_num):
        """
        校验剩余数量

        :param int item_id: 道具id
        :param int item_num: 道具数量
        """
        status = False

        if self.item:
            _, i, n = self.item

            if i == item_id and n >= item_num:
                status = True

        return status

    def reduce_item(self, item_id, item_num):
        """
        触发扣除道具

        :param int item_id: 道具id
        :param int item_num: 道具数量
        """
        if not self.item:
            raise Exception("大臣无道具")

        t, i, n = self.item

        if i != item_id:
            raise Exception("大臣道具不匹配")

        if n < item_num:
            raise Exception("大臣道具数量不足")

        # 扣道具
        n -= item_num

        self.item = [t, i, n]

    def has_item(self):
        """
        是否拥有道具
        """
        status = False

        if self.item:
            _, i, n = self.item

            if n >= 1:
                status = True

        return status

    def format_data(self):
        """
        格式化
        """
        return [
            self.id,
            self.rank,
            self.fight_power,
            self.max_hp,
            self.hp,
            self.item,
            self.adorn_guard_id,
            self.current_costume,
            self.refresh_time,
            self.equipment,
            self.guard_list,
            self.destiny_wake_lv,
            self.refresh_time_str,
        ]


class Team(object):
    """
    队伍
    """
    def __init__(self, rid, activity_id, seat_id, team_id):
        """
        当为NPC队伍时, rid=NPC, seat_id=0

        :param str rid: 角色rid
        :param str activity_id: 活动id
        :param int seat_id: 大本营位置
        :param str team_id: 队伍id
        """
        self.redis = RedisStorage.redis_conn_map['pluto']

        # 角色rid: str
        self.role_guid = rid
        # 活动id: str
        self.activity_id = activity_id
        # 国家位置: int
        self.seat_id = seat_id
        # 队伍id: str
        self.team_id = team_id
        # 区服id: int, 默认npc
        self.server_id = NpcServerId

        # 队伍类型: int
        self.team_type = None
        # x坐标: int
        self.x = None
        # y坐标: int
        self.y = None
        # 大臣数据 {str(index): [大臣id, 爵位, 战力, 最大血量, 当前血量, 道具, 守护, 皮肤, 复活时间,装备]}
        self.ministers = {}
        # 正在攻击中的地块队伍(NPC,防守): str
        self.atking_npc = None
        # 正在攻打的玩家攻击队伍: str
        self.atking_atk = None
        # npc队伍id, 默认玩家队伍: int
        self.npc_team_id = RoleTeamId
        # 活着的大臣id
        self.living_ministers = []

        # 自身天赋buff
        self.self_talent_buff = {}
        # 全队天赋buff
        self.team_talent_buff = {}

    def save(self):
        """
        保存队伍到缓存
        """
        assert self.team_type is not None
        assert self.x is not None
        assert self.y is not None

        # 更新队伍数据
        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        data = {
            "team_type": self.team_type,
            "x": self.x,
            "y": self.y,
            "ministers": json.dumps(self.ministers),
            "atking_npc": self.atking_npc if self.atking_npc is not None else "",
            "atking_atk": self.atking_atk if self.atking_atk is not None else "",
            "npc_team_id": self.npc_team_id,
            "server_id": self.server_id,
            "self_talent_buff": json.dumps(self.self_talent_buff),
            "team_talent_buff": json.dumps(self.team_talent_buff),
        }

        self.redis.hmset(role_team_key, data)

        # 更新队伍类型集合
        role_teams_key = CrossSiegelordCacheKey.RoleTeams.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            role_guid=self.role_guid,
            team_type=self.team_type,
        )
        self.redis.sadd(role_teams_key, self.team_short_key)

    def load(self):
        """
        从缓存中加载数据
        """
        # 获取队伍数据
        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        data = self.redis.hgetall(role_team_key)
        if not data:
            raise TeamNotFound()

        self.team_type = int(data["team_type"])
        self.x = int(data["x"])
        self.y = int(data["y"])
        self.ministers = json.loads(data["ministers"])
        self.atking_npc = data["atking_npc"] if data["atking_npc"] else None
        self.atking_atk = data["atking_atk"] if data["atking_atk"] else None
        self.npc_team_id = int(data["npc_team_id"])
        self.server_id = int(data["server_id"])

        self.self_talent_buff = json.loads(data["self_talent_buff"]) if data.get("self_talent_buff") else {}
        self.team_talent_buff = json.loads(data["team_talent_buff"]) if data.get("team_talent_buff") else {}

    def delete(self):
        """
        从缓存中删除队伍
        """
        # 删除队伍数据
        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        self.redis.delete(role_team_key)

        # 从队伍类型集合集合中移除
        role_teams_key = CrossSiegelordCacheKey.RoleTeams.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            role_guid=self.role_guid,
            team_type=self.team_type,
        )
        self.redis.srem(role_teams_key, self.team_short_key)

    @property
    def team_short_key(self):
        """
        用于缓存地块信息中队伍的短key

        seat_id:role_guid:team_id
        """
        return "{}:{}:{}".format(self.seat_id, self.role_guid, self.team_id)

    @property
    def status(self):
        """
        队伍状态

        大臣血量为0 >= 3
        """
        count = 0

        for _, minister_data in self.ministers.iteritems():
            minister = Minister(minister_data)
            if minister.hp <= 0:
                count += 1

        if count >= 3:
            return TeamStatus.Death
        else:
            return TeamStatus.Normal

    def refresh(self):
        """
        刷新队伍,主要是重置死亡大臣, NPC队伍不重置
        """
        if self.team_type == TeamType.Npc:
            return

        # 是否需要更新
        need_update = False
        new_ministers = {}

        for index, minister_data in self.ministers.iteritems():
            minister = Minister(minister_data)

            # 死亡且复活时间已到
            if ((minister.hp <= 0)
                    and (minister.refresh_time != 0)
                    and (minister.refresh_time <= int(to_timestamp(get_ctx_time())))):
                need_update = True

                # 重置血量
                minister.hp = minister.max_hp
                # 重置复活时间
                minister.refresh_time = 0
                minister.refresh_time_str = "2010-01-01 00:00:00"

            new_ministers[index] = minister.format_data()

            # 添加到或者的大臣列表
            if (minister.hp > 0) and (minister.id not in self.living_ministers):
                self.living_ministers.append(minister.id)

        if need_update:
            self.ministers = new_ministers

    def get_minister_data(self, index):
        """
        获取大臣数据

        :param int index: 大臣位置
        """
        return self.ministers[str(index)]

    def load_atking_npc_team(self):
        """
        加载正在打的NPC队伍
        """
        npc_team = None
        if self.atking_npc:
            seat_id, rid, team_id = self.atking_npc.split(":")
            # 获取队伍数据
            npc_team = Team(
                rid=rid,
                activity_id=self.activity_id,
                seat_id=int(seat_id),
                team_id=team_id,
            )
            try:
                npc_team.load()
            except TeamNotFound:
                # 队伍已被击溃
                self.atking_npc = None
                npc_team = None

        return npc_team

    def asdict(self):
        """
        客户端
        """
        return {
            "TeamId": self.team_id,
            "TeamType": self.team_type,
            "X": self.x,
            "Y": self.y,
            "Ministers": self.ministers,
            "AtkingNPC": self.atking_npc,
            "AtkingAtk": self.atking_atk,
            "TeamStatus": self.status,
            "NpcTeamId": self.npc_team_id,
            "RoleGuid": self.role_guid,
            "ServerId": self.server_id,
            "SelfTalentBuff": self.self_talent_buff,
            "TeamTalentBuff": self.team_talent_buff,
        }

    def ministers_with_item(self):
        """
        埋点数据: 大臣和道具
        """
        results = []

        for index, _ in enumerate(range(TeamCount), start=1):
            minister = Minister(self.get_minister_data(index))

            data = {
                "MinisterId": minister.id,
                "Item": minister.item,
            }

            results.append(data)

        return results

    def ministers_with_hp(self, ministers_data=None):
        """
        埋点数据: 大臣和血量
        """
        results = []

        for index, _ in enumerate(range(TeamCount), start=1):
            if ministers_data:
                minister = Minister(ministers_data[str(index)])
            else:
                minister = Minister(self.get_minister_data(index))

            data = {
                "MinisterId": minister.id,
                "HP": minister.hp,
            }

            results.append(data)

        return results

    # #################
    # 单独更新方法
    # #################
    def update_ministers(self, ministers):
        """
        更新大臣数据

        :param dict ministers: 大臣数据
        """
        self.ministers = ministers

        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        self.redis.hset(role_team_key, "ministers", json.dumps(ministers))

    def update_location(self, x, y):
        """
        更新队伍坐标
        """
        self.x = x
        self.y = y

        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        self.redis.hset(role_team_key, "x", x)
        self.redis.hset(role_team_key, "y", y)

    def update_atking_npc(self, atking_npc):
        """
        更新正在攻打的npc队伍
        """
        self.atking_npc = atking_npc

        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        if atking_npc is None:
            atking_npc = ""

        self.redis.hset(role_team_key, "atking_npc", atking_npc)

    def update_atking_atk(self, atking_atk):
        """
        更新正在攻打的进攻队伍
        """
        self.atking_atk = atking_atk

        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        if atking_atk is None:
            atking_atk = ""

        self.redis.hset(role_team_key, "atking_atk", atking_atk)

    def update_self_talent_buff(self, self_talent_buff):
        """
        更新自身天赋buff
        """
        self.self_talent_buff = self_talent_buff

        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        self.redis.hset(role_team_key, "self_talent_buff", json.dumps(self_talent_buff))

    def update_team_talent_buff(self, team_talent_buff):
        """
        更新全队天赋buff
        """
        self.team_talent_buff = team_talent_buff

        role_team_key = CrossSiegelordCacheKey.RoleTeam.format(
            app_id=get_app_id(),
            activity_id=self.activity_id,
            seat_id=self.seat_id,
            role_guid=self.role_guid,
            team_id=self.team_id,
        )

        self.redis.hset(role_team_key, "team_talent_buff", json.dumps(team_talent_buff))
